package mo_gin

import (
	"MoSkeleton/framework/core/consts"
	"MoSkeleton/framework/core/dto/response"
	"MoSkeleton/framework/mo_gin/middleware/cors"
	"MoSkeleton/framework/utils"
	"errors"
	"fmt"
	"github.com/fvbock/endless"
	"github.com/gin-contrib/pprof"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"time"
)

type webServer interface {
	ListenAndServe() error
}

type MoGinConfig struct {
	router *gin.Engine
}

func (g *MoGinConfig) InitGin() (*gin.Engine, error) {
	g.router = gin.New()
	g.router.Use(gin.Logger(), g.ginRecovery())
	if CommonGin.Mode == "" {
		gin.SetMode(gin.ReleaseMode)
	} else {
		gin.SetMode(CommonGin.Mode)
	}
	if CommonGin.Mode != consts.ReleaseMode && CommonGin.Pprof.Enable {
		MoLogger.Info("pprof enabled")
		if CommonGin.Pprof.Path == "" {
			pprof.Register(g.router)
		} else {
			pprof.Register(g.router, CommonGin.Pprof.Path)
		}
	}
	if err := g.initGinEngine(g.router); err != nil {
		return nil, err
	}
	return g.router, nil
}

func (g *MoGinConfig) StartServer() {
	address := CommonGin.Server.Addr
	if address == "" {
		address = ":2457"
	}
	server := g.initServer(address)
	MoLogger.Info("server startup on :", zap.String("address", address))
	if err := server.ListenAndServe(); err != nil {
		MoLogger.Error("server startup error", zap.Error(err))
	}
}

func (g *MoGinConfig) initGinEngine(router *gin.Engine) error {
	if CommonGin.Proxy.Enable {
		if err := router.SetTrustedProxies(CommonGin.Proxy.ServerList); err != nil {
			return err
		}
	}
	//跨域
	if CommonGin.Cors.AllowCors {
		router.Use(cors.NextByConfig(&CommonGin.Cors))
	}
	//multipart
	var err error
	if router.MaxMultipartMemory, err = utils.ConvertToSizeInt64(CommonGin.Multipart.MaxMemory); err != nil {
		return err
	}
	return nil
}

func (g *MoGinConfig) initServer(address string) webServer {
	s := endless.NewServer(address, g.router)
	s.ReadHeaderTimeout = 10 * time.Minute
	s.WriteTimeout = 10 * time.Minute
	s.MaxHeaderBytes = 1 << 20
	return s
}

func (g *MoGinConfig) ginRecovery() gin.HandlerFunc {
	DefaultErrorWriter := &PanicExceptionWriter{}
	return gin.RecoveryWithWriter(DefaultErrorWriter, func(c *gin.Context, err interface{}) {
		// 这里针对发生的panic等异常进行统一响应即可
		// 这里的 err 数据类型为 ：runtime.boundsError  ，需要转为普通数据类型才可以输出
		response.ErrorSystem(c, "", fmt.Sprintf("%s", err))
	})
}

// PanicExceptionWriter  panic等异常记录
type PanicExceptionWriter struct{}

func (p *PanicExceptionWriter) Write(b []byte) (n int, err error) {
	errStr := string(b)
	err = errors.New(errStr)
	msg, _ := Translater.Translate(consts.TKey_InternalError)
	MoLogger.Error(msg, zap.Error(err))
	fmt.Printf("%+v", err)
	return len(errStr), err
}
